.. _Lvalues и Rvalues:

Lvalues и Rvalues
=================

.. index::
    lvalues
    rvalues

Значение каждого выражения классифицируется как lvalue или rvalue. A
простой способ различать эти два понятия - считать lvalues фактическими переменными
(включая элементы массивов и ассоциативных массивов), а rvalues - как временные
временными результатами выражений (включая литералы).
В качестве демонстрации, первое выражение writeln() ниже использует только lvalues
а во втором - только rvalues:

.. code-block:: d

    import std.stdio;

    void main()
    {
        int i;
        immutable(int) imm;
        auto arr = [1];
        auto aa = [10: "ten"];
        /* All of the following arguments are lvalues. */
        writeln(i, // mutable variable
            imm, // immutable variable
            arr, // array
            arr[0], // array element
            aa[10]); // associative array element
        // etc.

        enum message = "hello";
        /* All of the following arguments are rvalues. */
        writeln(42, // a literal
            message, // a manifest constant
            i + 1, // a temporary value
            calculate(i)); // return value of a function
        // etc.
    }

    int calculate(int i)
    {
        return i * 2;
    }


Ограничения rvalues
-------------------

По сравнению с lvalues, rvalues имеют следующие три ограничения.

**У rvalues нет адресов памяти**

Правые значения не имеют адресов
Левые значения могут иметь место в памяти, а правые - нет.
Например, правое значение a + b в следующей программе не может быть адресовано:

.. code-block:: d

    import std.stdio;

    void main()
    {
        int a;
        int b;
        readf(" %s", &a); // ← компилируется
        readf(" %s", &(a + b)); // ← ошибка компиляции
    }

.. code-block:: console

    Error: a + b is not an lvalue

**rvalue нельзя присваивать новые значения**

Если  lvalue можно изменить, то ему можно присвоить новое значение, а  rvalue - нет:

.. code-block:: d

    a = 1; // ← компилируется
    (a + b) = 2; // ← ошибка компиляции

.. code-block:: console

     Error: a + b is not an lvalue

**rvalue нельзя передавать в функции по ссылке**

lvalue можно передать в функцию, которая принимает параметр по ссылке, в то время как rvalue нельзя.

.. code-block:: d

    void incrementByTen(ref int value)
    {
        value += 10;
    }
    // ...

    incrementByTen(a); // ← компилируется
    incrementByTen(a + b); // ← ошибка компиляции


.. code-block:: console

    Error: function deneme.incrementByTen (ref int value) is not callable using argument types (int)

Основная причина этого ограничения заключается в том, что функция, принимающая параметр ref
может сохранить эту ссылку для последующего использования, в то время, когда значение rvalue будет недоступно.
В отличие от таких языков, как C++, в D rvalue не может быть передано в функцию
функции, даже если эта функция не изменяет аргумент:

.. code-block:: d

    void print(ref const(int) value)
    {
        writeln(value);
    }
    // ...
    print(a); // ← компилируется
    print(a + b); // ← ошибка компиляции

.. code-block:: console

    Error: function deneme.print (ref const(int) value) is not callable using argument types (int)


Использование параметров auto ref для приема как lvalues, так и rvalues
-----------------------------------------------------------------------

Как уже говорилось в предыдущей главе, параметры auto ref шаблонов функций(стр. 399) могут принимать как lvalues, так и rvalues.
Когда аргумент является lvalue, auto ref означает "по ссылке". С другой
с другой стороны, поскольку rvalues нельзя передавать в функции по ссылке, когда аргумент
аргумент является rvalue, auto ref означает copy. Чтобы компилятор генерировал код
по-разному для этих двух разных случаев, функция должна быть шаблоном.
Шаблоны мы рассмотрим в одной из следующих глав. Пока же смиритесь с тем, что
выделенные пустые круглые скобки ниже делают следующее определение функцией
template.

.. code-block:: d

    void incrementByTen()(auto ref int value)
    {
        /* WARNING: The parameter may be a copy if the argument is
        * an rvalue. This means that the following modification
        * may not be observable by the caller. */
        value += 10;
    }

    void main()
    {
        int a;
        int b;
        incrementByTen(a); // ← lvalue; passed by reference
        incrementByTen(a + b); // ← rvalue; copied
    }


Определить, является ли параметр l-значением или r-значением, можно  используя __traits(isRef) со  статическим if :

.. code-block:: d

    void incrementByTen()(auto ref int value)
    {
        static if (__traits(isRef, value)) {
        // 'value' is passed by reference
        } else {
        // 'value' is copied
        }
    }

.. index::
    __traits

Со статическими if и __traits мы познакомимся позже, в главе Условная компиляция  (стр. 460).

Терминология
------------

Названия "lvalue" и "rvalue" не совсем точно отражают характеристики этих двух
не совсем точно отражают характеристики этих двух видов значений. Начальные буквы l и r идут слева и справа,
обозначая лево- и правосторонние выражения оператора присваивания
оператора присваивания:
* Предполагая, что значение l является изменяемым, оно может быть левым выражением операции присваивания.
операции присваивания.
* Значение r не может быть левым выражением операции присваивания.
Термины "левое значение" и "правое значение" путают, потому что в общем случае и
l-значения и r-значения могут находиться по обе стороны от операции присваивания:

.. code-block:: d

    // rvalue 'a + b' on the left, lvalue 'a' on the right:
    array[a + b] = a;
